package kp.rpc.fault.tolerant.impl;

import cn.hutool.core.collection.CollUtil;
import kp.rpc.RpcApplication;
import kp.rpc.fault.retry.RetryStrategy;
import kp.rpc.fault.retry.factory.RetryStrategyFactory;
import kp.rpc.fault.tolerant.TolerantStrategy;
import kp.rpc.loadbalancer.LoadBalancer;
import kp.rpc.loadbalancer.factory.LoadBalancerFactory;
import kp.rpc.model.RpcRequest;
import kp.rpc.model.RpcResponse;
import kp.rpc.model.ServiceMetaInfo;
import kp.rpc.server.client.VertxTcpClient;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

@Slf4j
public class FailOverTolerantStrategy implements TolerantStrategy {
    @Override
    public RpcResponse doTolerant(Map<String, Object> context, Exception e) throws Exception {
        //todo 故障转移容错
        @SuppressWarnings("unchecked")
        List<ServiceMetaInfo> failTolerantList = (List<ServiceMetaInfo>) context.get("failTolerantList");
        ServiceMetaInfo failServiceMetaInfo = (ServiceMetaInfo) context.get("failServiceMetaInfo");
        //重新选取,排除掉fail的
        if (!CollUtil.isEmpty(failTolerantList) && !Objects.isNull(failServiceMetaInfo)) {
            Iterator<ServiceMetaInfo> iterator = failTolerantList.iterator();
            while (iterator.hasNext()) {
                ServiceMetaInfo metaInfo = iterator.next();
                boolean flag = metaInfo.getServiceNodeKey().equals(failServiceMetaInfo.getServiceNodeKey());
                if (flag) {
                    iterator.remove();
                }
            }
        }
        RpcRequest request = (RpcRequest) context.get("rpcRequest");
        String loadBalancer = RpcApplication.getRpcConfig().getLoadBalancer();
        LoadBalancer loadBalancerImpl = LoadBalancerFactory.getLoadBalancer(loadBalancer);
        HashMap<String, Object> requestParams = new HashMap<>();
        requestParams.put("method", request.getMethodName());
        ServiceMetaInfo selectServiceMetaInfo = loadBalancerImpl.select(requestParams, failTolerantList);
        RetryStrategy retryStrategy = RetryStrategyFactory.getInstance(RpcApplication.getRpcConfig().getRetryStrategy());
        return retryStrategy.doRetry(() -> VertxTcpClient.doRpcResponse(request, selectServiceMetaInfo));
    }
}
